package sidecar

import (
	su "lqq/sidecar/utils"
	"time"
)

type service struct {
	msgChan         chan *requestMsg
	canUse          bool
	lastRefreshTime time.Time
	//instMu          sync.Mutex
	//insts           *list.List
	//notUseMu        sync.Mutex
	//notUseInsts     []*serviceInst
	instsStore   *su.QueneStore
	uselessInsts *su.QueneStore
}

func newService() *service {
	s := &service{
		msgChan:         make(chan *requestMsg, 500),
		canUse:          false,
		lastRefreshTime: time.Now(),
		// instMu:          sync.Mutex{},
		// insts:           list.New().Init(),
		// notUseMu:        sync.Mutex{},
		// notUseInsts:     make([]*serviceInst, 0),
		instsStore:   su.NewQueneStore(),
		uselessInsts: su.NewQueneStore(),
	}
	go s.monitorMsgChan()
	//go s.monitorRefreshByRegisterServer()
	return s
}

// func (s *service) monitorRefreshByRegisterServer() {
// 	for {
// 		select {
//         case <-time.After(time.Second * 10):

//         }
// 	}
// }

func (s *service) destroy() {
	s.canUse = false
	//
	所有serverInst.destroy()
}

func (s *service) refreshInstance(addrs []string) {
	for _, addr := range addrs {
		_, ok1 := s.instsStore.Get(addr)
		_, ok2 := s.uselessInsts.Get(addr)
		if !ok1 && !ok2 {
			inst, err := newServiceInst(s, su.UUID(), addr)
			if err != nil {
				logger.error(err.Error())
				continue
			}
			s.instsStore.Put(addr, inst)
		}
	}

}

func (s *service) monitorMsgChan() {
	for {
		select {
		case msg := <-s.msgChan:
			for i := 0; i < 3; i++ {
				inst := s.getServiceInst()
				if inst != nil {
					if inst.canUse {
						//实例不为空且状态正常
						//直接放入该实例等待发送
						inst.writeRequest(msg)
						s.addInst(inst) //将该实例放回服务等待下次使用
						break
					} else {
						//实例不为空但处于不可用状态 将该实例放入不可用列表等待重新连接。如果
						//重新连接仍然不可用，即放弃该实例将其丢掉。并且丢掉的实例应该放入
						//“已丢弃集合”中，注明丢掉的时间，3秒后才允许再次检查是否可用
						s.addNotUseInst(inst)
						s.putRequestMsg(msg) //直接放入该实例等待发送
						break
					}
				} else {
					//实例为空，表示服务没有任何可用的实例
					//当服务没有可用实例时，注明该服务不可用，并且所有请求该服务
					//的请求都会返回错误
					h := &RequestHeader{
						StatusCode: NETWORK_ERR,
						TRACEID:    msg.reqHeader.TRACEID,
						REQID:      msg.reqHeader.REQID,
					}
					msg.respHeader = h
					msg.semaphore <- true
				}
			}

		}
	}
}

func (s *service) addInst(inst *serviceInst) {
	s.instsStore.Put(inst.addr, inst)
	s.canUse = true
}

func (s *service) getServiceInst() *serviceInst {
	if s.canUse {
		a, ok := s.instsStore.GetOne()
		if ok {
			return a.(*serviceInst)
		}
	}
	return nil
}
func (s *service) addNotUseInst(inst *serviceInst) {
	s.uselessInsts.Put(inst.addr, inst)
	go inst.reConnect(false)
}

func (s *service) removeNotUseInst(inst *serviceInst) {
	s.uselessInsts.Remove(inst.addr)
}

func (s *service) putRequestMsg(r *requestMsg) {
	s.msgChan <- r
}
